﻿namespace BM.Tools.WebControls.MForm.BlogExample
{
    using System;    
    using System.ComponentModel;
    using System.Security;
    using System.Xml;
    using System.Xml.Serialization;

    using BM.Tools.WebControls.MForm.Controls.Items;
    using BM.Tools.WebControls.MForm.Generator;
    
    using Controls.Modifiers;

    /// <summary>
    /// A control created using the MForm custom controls for creating and modifying blog entries and comments.
    /// </summary>
    public partial class BlogControl : System.Web.UI.UserControl
    {
        /// <summary>
        /// Gets or sets the author of the currently performed modification.
        /// </summary>
        [Bindable(true)]
        public string Author
        {
            get
            {
                return ViewState["author"] as string;
            }

            set
            {
                ViewState["author"] = value;
            }
        }

        /// <summary>
        /// Gets or sets the BlogEntry object that is edited using the control.
        /// </summary>
        public BlogEntry BlogEntry
        {
            get
            {
                var blogEntrySerializer = new XmlSerializer(typeof(BlogEntry));
                using (var reader = new XmlNodeReader(Form.OutputXml))
                {
                    return blogEntrySerializer.Deserialize(reader) as BlogEntry;
                }
            }

            set
            {
                Form.DataSource = value.ToXPathDocument();
            }
        }

        /// <summary>
        /// Gets or sets the mode of the control - different modes represent different control behaviors.
        /// </summary>
        [DefaultValue(BlogControlMode.Viewing)]
        public BlogControlMode Mode
        {
            get
            {
                if (ViewState["mode"] == null)
                {
                    return BlogControlMode.Viewing;
                }

                return (BlogControlMode)ViewState["mode"];
            }

            set
            {
                ViewState["mode"] = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is prepared.
        /// </summary>
        protected bool IsPrepared
        {
            get;
            set;
        }

        /// <summary>
        /// Binds a data source to the invoked server control and all its child controls.
        /// </summary>
        public override void DataBind()
        {            
            base.DataBind();
            Prepare();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data. </param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            EnsurePrepared();
        }

        /// <summary>
        /// Performs changes needed for comment adding:
        /// disabling blog entry title and text,
        /// setting all other comments modes to view,
        /// preparing an additional comment,
        /// hiding the add button and removal buttons,
        /// setting the author and date,
        /// setting the default comment title,
        /// </summary>
        protected virtual void PrepareCommentAdding()
        {
            SetTitleAndTextToViewMode();
            var commentBasis = CommentsItem.SelectSingle("Comment").Basis;
            foreach (var commentItem in commentBasis.Items)
            {
                commentItem.Mode = FormBuilderMode.View;
            }

            commentBasis.AddButton.Visible = false;
            var newCommentItem = commentBasis.AddItem(null) as BranchItem;
            SectionDisplayModifier.ExpandOffspring(newCommentItem);
            newCommentItem.RemovalDisabled = true;
            LeafItem authorItem = GetAuthorItem(newCommentItem);
            authorItem.Value = Author;
            var dateItem = newCommentItem.SelectSingle("Date") as LeafItem;
            dateItem.Value = DateTime.Now.ToShortDateString();
            var commentTitleItem = GetTitleItem(newCommentItem);
            commentTitleItem.Value = string.Concat("Re: ", TitleItem.Value);            
        }

        /// <summary>
        /// Performs changes needed for comment editing:
        /// disabling blog entry title and text,
        /// hiding the add button,
        /// setting the comments of the of the current author to edit mode and expanding them,
        /// </summary>
        protected virtual void PrepareCommentEditing()
        {
            SetTitleAndTextToViewMode();
            foreach (BranchItem commentItem in CommentsItem.Select("Comment"))
            {
                commentItem.Basis.AddButton.Visible = false;
                var isCommentOwner = Author == GetAuthorItem(commentItem).Value;
                commentItem.Mode = isCommentOwner ? FormBuilderMode.Edit : FormBuilderMode.View;
                if (isCommentOwner)
                {
                    SectionDisplayModifier.ExpandOffspring(commentItem);
                }
            }
        }

        /// <summary>
        /// Performs changes needed for entry adding:
        /// hiding the comments,
        /// setting date and author value,
        /// </summary>
        protected virtual void PrepareEntryAdding()
        {
            CommentsItem.Visible = false;
            DateItem.Value = DateTime.Now.ToShortDateString();
            AuthorItem.Value = Author;
        }

        /// <summary>
        /// Performs changes needed for entry adding:
        /// checking if the blog control current author and the edited blog entry author match,
        /// changing the comments mode to view,
        /// </summary>
        /// <exception cref="SecurityException">
        /// </exception>
        protected virtual void PrepareEntryEditing()
        {
            if (Author != AuthorItem.Value)
            {
                throw new SecurityException("The declared author and the author of the blog entry do not match.");
            }

            CommentsItem.Mode = FormBuilderMode.View;
        }

        /// <summary>
        /// Performs changes needed for viewing:
        /// changing the form mode to view,
        /// changing the title control's text to <see cref="BlogEntry"/> <see cref="DataEntry.Title"/>,
        /// changing the comment controls' title control text to <see cref="Comment"/> <see cref="DataEntry.Title"/>
        /// </summary>
        protected virtual void PrepareViewing()
        {
            Form.Mode = FormBuilderMode.View;
            AddTitleTextToTitle();
            foreach (BranchItem commentItem in CommentsItem.Select("Comment"))
            {
                var commentTitleItem = GetTitleItem(commentItem);
                commentItem.Title.Text = commentTitleItem.Value;
                commentTitleItem.Visible = false;
            }
        }

        private void EnsurePrepared()
        {
            if (!IsPrepared)
            {
                Prepare();                
            }
        }

        private void Prepare()
        {
            switch (Mode)
            {
                case BlogControlMode.CommentAdding:
                    PrepareCommentAdding();
                    break;
                case BlogControlMode.CommentEditing:
                    PrepareCommentEditing();
                    break;
                case BlogControlMode.EntryAdding:
                    PrepareEntryAdding();
                    break;
                case BlogControlMode.EntryEditing:
                    PrepareEntryEditing();
                    break;
                case BlogControlMode.Viewing:
                    PrepareViewing();
                    break;
            }

            IsPrepared = true;
        } 

        private void SetTitleAndTextToViewMode()
        {
            AddTitleTextToTitle();
            TextItem.Mode = FormBuilderMode.View;
        }

        private void AddTitleTextToTitle()
        {
            TitleItem.Visible = false;
            BlogEntryItem.Title.Text = TitleItem.Value;
        }

        private LeafItem GetAuthorItem(BranchItem commentItem)
        {
            return commentItem.SelectSingle("Author") as LeafItem;
        }

        private LeafItem GetTitleItem(BranchItem commentItem)
        {
            return commentItem.SelectSingle("Title") as LeafItem;
        }
    }
}